#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/eventfd.h>
#include <sys/epoll.h>
#include <utils.h>
#include <dlist.h>
#include <evtloop.h>
#include <async.h>
#include <printk.h>

static void svc__async_send(ev_loop_t *loop);
static int svc__async_eventfd(void);

int svc_async_init(ev_loop_t *loop, svc_async_t *handle,
                   svc_async_cb async_cb, void *data)
{
    memset(handle, 0, sizeof(svc_async_t));
    handle->loop = loop;
    handle->async_cb = async_cb;
    handle->data = data;

    list_add_tail(&handle->obj.entry, &loop->async_head);
    ev__handle_start(handle);
    return 0;
}

int svc_async_send(svc_async_t *handle)
{
    /* Do a cheap read first. */
    if (ACCESS_ONCE(int, handle->pending) != 0)
        return 0;

    if (__sync_val_compare_and_swap(&handle->pending, 0, 1) == 0)
        svc__async_send(handle->loop);

    return 0;
}

void svc_async_close(svc_async_t *handle)
{
    ev__handle_stop(handle);
    list_del(&handle->obj.entry);
}

static svc_async_t *async_next(ev_loop_t *loop)
{
    object_t *o;

    list_for_each_entry(o, &loop->async_head, entry)
    {
        svc_async_t *h = (svc_async_t *)o;

        if (__sync_val_compare_and_swap(&h->pending, 1, 0) == 1)
            return h;
    }
    return NULL;
}
static void svc__async_io(ev_io_t *io, u32 e)
{
    ev_loop_t *loop = container_of(io, ev_loop_t, async_io);

    for (;;)
    {
        char buf[8];
        ssize_t r = read(loop->async_io.fd, buf, sizeof(buf));
        if (r != -1)
            break;

        if (errno == EAGAIN || errno == EWOULDBLOCK)
            break;

        if (errno == EINTR)
            continue;

        assert(0);
    }

    for (;;)
    {
        svc_async_t *h = async_next(loop);
        if (!h)
            break;
        if (h->async_cb)
            h->async_cb(h);
    }
}

static void svc__async_send(ev_loop_t *loop)
{
    int r;
    static const u64 val = 1;

    assert(loop);
    do
        r = write(loop->async_io.fd, &val, sizeof(val));
    while (r == -1 && errno == EINTR);

    if (r == sizeof(val))
        return;

    if (r == -1)
        if (errno == EAGAIN || errno == EWOULDBLOCK)
            return;
    assert(0);
}

int svc__async_start(ev_loop_t *loop)
{
    if (loop->async_io.fd < 0)
    {
        int err = svc__async_eventfd();
        if (err < 0) return err;

        ev__io_init(&loop->async_io, svc__async_io, err);
        ev__io_start(loop, &loop->async_io, EPOLLIN);
    }
    return 0;
}

void svc__async_stop(ev_loop_t *loop)
{
    if (loop->async_io.fd < 0)
        return;

    ev__io_stop(loop, &loop->async_io, EVT_ALL);
    close(loop->async_io.fd);
    loop->async_io.fd = -1;
}

static int svc__async_eventfd(void)
{
    int fd;

    fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
    return fd < 0 ? -ENXIO : fd;
}
